#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <pthread.h>

#define IP "172.31.173.225"
#define UDP_IP  "172.31.173.255"
#define TCP_PROT 8888
#define UDP_PROT 9999

void* thread_tcp(void *arg);
void* thread_udp(void *arg);
void* thread_tcp_qt(void *arg);
int main()
{
    pthread_t  tcp_tid,udp_tid;

    pthread_create(&tcp_tid,NULL,&thread_tcp,NULL);
    pthread_create(&udp_tid,NULL,&thread_udp,NULL);    

    pthread_join(tcp_tid,NULL);
    pthread_join(udp_tid,NULL);
    return 0;
}

void* thread_tcp(void *arg)
{
    
    int tcp_sockid = socket(AF_INET,SOCK_STREAM,0);
    if( -1 == tcp_sockid)
    {
        perror("tcp_sockid");
        return NULL;
    }
    printf("tcp_sockid %d\n",tcp_sockid);

    int reuseport = 1;
    if(-1 == setsockopt(tcp_sockid, SOL_SOCKET, SO_REUSEADDR, &reuseport, sizeof(reuseport)))
    {
        perror("setsockopt");
        return NULL;
    }
    struct sockaddr_in tcp_addr;
    tcp_addr.sin_family= AF_INET;
    tcp_addr.sin_port =  htons(TCP_PROT);
    int ret= inet_aton(IP,&tcp_addr.sin_addr);
    if(ret==0)
    {
        perror("inet_aton");
        close(tcp_sockid);
        return NULL;
    }
    ret = bind(tcp_sockid,(struct sockaddr*)&tcp_addr,sizeof(tcp_addr));
    if(ret == -1)
    {
        perror("bind");
        close(tcp_sockid);
        return NULL;
    }
    ret = listen(tcp_sockid,5);
    if(-1 == ret)
    {
        perror("listen");
        close(tcp_sockid);
        return NULL;
    }
    while(1)
    {
        int act_tcp = accept(tcp_sockid,NULL,NULL);
        if(act_tcp == -1)
        {
            perror("accept");
            close(tcp_sockid);
            close(act_tcp);
            return NULL;
        }


        //心跳检测
        struct timeval tv; //用于设置超时结构体
        fd_set readfds;//文件描述符合集
        int heartbeat_timeout =10;//心跳超时时间
        int heartbeat_count =0;//心跳计数器
        char buf[1024];
        while(1)
        {
            //设置超时时间
            tv.tv_sec =heartbeat_timeout;
            tv.tv_usec=0;

            FD_ZERO(&readfds);
            FD_SET(act_tcp,&readfds);
            
            int sel=select(act_tcp+1,&readfds,NULL,NULL,&tv);

            if(sel ==-1)
            {
                perror("select error");
                break;
            }else if(sel == 0)
            {
                //链接超时
                printf("心跳超时，客户端可能已断开\n");
                continue;
            }else
            {
                if(FD_ISSET(act_tcp,&readfds))
                {
                    
                    int ret = recv(act_tcp,buf,sizeof(buf),0);
                    if(ret == -1)
                    {
                        perror("recv error");
                        close(act_tcp);
                        close(tcp_sockid);
                        break;
                    }else  if(ret ==0){
                        printf("连接断开\n");
                        break;
                        
                    }else{
                        printf("保持连接\n");

                    }
                }
                memset(buf,0,sizeof(buf));
            }
            
        }
        close(act_tcp);
    }
    
}


void* thread_udp(void *arg)
{
   
    int ret;
    int udp_sockid = socket(AF_INET,SOCK_DGRAM,0);
    if(udp_sockid <0)
    {
        perror("udp_sockid");
        return 0;
    }
    printf("udpid %d\n",udp_sockid);
    // 2. 添加广播权限
    int on = 1;
    if(-1 == setsockopt(udp_sockid, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
    {
        perror("setsockopt");
        return NULL;
    }
    // 3. 设置允许端口复用
    int reuseport = 1;
    if(-1 == setsockopt(udp_sockid, SOL_SOCKET, SO_REUSEADDR, &reuseport, sizeof(reuseport)))
    {
        perror("setsockopt");
        return NULL;
    }
    struct sockaddr_in udp_addr ;
    memset(&udp_addr, 0, sizeof(udp_addr));
    udp_addr.sin_family= AF_INET;
    udp_addr.sin_port =  htons(UDP_PROT);
    //udp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = inet_aton(UDP_IP,&udp_addr.sin_addr);
    if(!ret){
        perror("inet_aton");
    }
     if (bind(udp_sockid, (const struct sockaddr *)&udp_addr, sizeof(udp_addr)) < 0) {
        perror("UDP bind failed");
        exit(-1);
    }

    char udp_buf[1024];
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);
    while(1)
    {
        memset(udp_buf, 0, sizeof(udp_buf));
        memset(&client_addr, 0, sizeof(client_addr));
        ret =recvfrom(udp_sockid,udp_buf,sizeof(udp_buf),0,(struct sockaddr *)&client_addr,&client_len);
        if(ret < 0) {
            perror("recvfrom");
            continue;
        }
        printf("udp_buf:%s\n",udp_buf);

        if(strcmp(udp_buf,"coffee")!=0)
        {
            continue;
        }
        ret =sendto(udp_sockid,"yes",4,0,(struct sockaddr *)&client_addr,client_len);
        if(ret <0)
        {
            printf("发送失败\n");
        }else{
            printf("发送成功\n");
            pthread_t tcp_qtid;
            pthread_create(&tcp_qtid,NULL,&thread_tcp_qt,NULL);    

        
        }

        
        
    }
    close(udp_sockid);
    return NULL;
} 
    
void* thread_tcp_qt(void *arg)
{
    pthread_detach(pthread_self());
    int tcp1_sockid,act1_tcp;
    tcp1_sockid = socket(AF_INET,SOCK_STREAM,0);
    if(-1==tcp1_sockid){
        perror("socket");
        return NULL;
    }
    int reuseport = 1;
    if(-1 == setsockopt(tcp1_sockid, SOL_SOCKET, SO_REUSEADDR, &reuseport, sizeof(reuseport)))
    {
        perror("setsockopt");
        return NULL;
    }
    struct sockaddr_in tcp_addr;
    tcp_addr.sin_family= AF_INET;
    tcp_addr.sin_port =  htons(UDP_PROT);
    int ret= inet_aton(IP,&tcp_addr.sin_addr);
    if(ret==0)
    {
        perror("inet_aton");
        close(tcp1_sockid);
        return NULL;
    }
    ret = bind(tcp1_sockid,(struct sockaddr*)&tcp_addr,sizeof(tcp_addr));
    if(ret == -1)
    {
        perror("bind");
        close(tcp1_sockid);
        return NULL;
    }
    ret = listen(tcp1_sockid,5);
    if(-1 == ret)
    {
        perror("listen");
        close(tcp1_sockid);
        return NULL;
    }   
    
    
    act1_tcp = accept(tcp1_sockid,NULL,NULL);
    if(act1_tcp == -1)
    {
        perror("accept");
        close(tcp1_sockid);
        close(act1_tcp);
        return NULL;
    }
    printf("act1:%d\n",act1_tcp);
    
    char buff[1024]={0};
    memset(buff,0,sizeof(buff));
    FILE *fp; 
    ret=recv(act1_tcp,buff,sizeof(buff),0);
    if(0==ret){
        printf("disconnected\n");
        
    }else if(-1 == ret){
        perror("recv");
        exit(-1);
    }
    fp= fopen(buff,"w");
    if(NULL == fp){
        printf("文件打开失败\n");
        exit(-1);
    }
    memset(buff,0,sizeof(buff));
    while(1){
        ret=recv(act1_tcp,buff,sizeof(buff),0);
        
        if(0==ret){
            printf("disconnected\n");
            break;
        }else if(-1 == ret){
            perror("recv");
            exit(-1);
        }else{

            printf("bye:%s\n",buff);
            fputs(buff,fp);
            fflush(fp);
            
        }  
            
        
    }
        
    
    close(tcp1_sockid);
    close(act1_tcp);
    return NULL;   
}
     
